﻿using System;
using System.Linq;
using CcrFlows.Internal.Stages;
using Microsoft.Ccr.Core;

namespace CcrFlows.Core
{
    public static class Flow<TIn>
    {
        public static Flow<TIn, TOut> Do<TOut>(Func<TIn, TOut> handler)
        {
            return Do<TOut>((i, po) => po.Post(handler(i)));
        }

        public static Flow<TIn, TOut> Do<TOut>(ISeqHandler<TIn, TOut> handler)
        {
            return Do<TOut>(handler.Handle);
        }

        public static Flow<TIn, TOut> Do<TOut>(SeqHandler<TIn, TOut> handler)
        {
            return new Flow<TIn, TOut>(handler);
        }

        public static Flow<TIn, TOut> DoWithIterator<TOut>(SeqIteratorHandler<TIn, TOut> handler)
        {
            return new Flow<TIn, TOut>(handler);
        }


        public static ForkedFlow<TIn, TBranches, TBranches> Fork<TBranches>(ForkHandler<TIn, TBranches, TBranches> handler)
        {
            return Fork<TBranches, TBranches>(handler);
        }

        public static ForkedFlow<TIn, TBranch0, TBranch1> Fork<TBranch0, TBranch1>(ForkHandler<TIn, TBranch0, TBranch1> handler)
        {
            return new ForkedFlow<TIn, TBranch0, TBranch1>(handler);
        }



        public static Flow<TIn, TOut> Scatter<TOut>(ISeqHandler<TIn, TOut> handler)
        {
            return Scatter<TOut>(handler.Handle);
        }

        public static Flow<TIn, TOut> Scatter<TOut>(Func<TIn, TOut> handler)
        {
            return Scatter<TOut>((i, po) => po.Post(handler(i)));
        }

        public static Flow<TIn, TOut> Scatter<TOut>(SeqHandler<TIn, TOut> handler)
        {
            return new Flow<TIn, TOut>(new ScatterStage<TIn, TOut>(handler));
        }

        public static Flow<TIn, TOut> ScatterWithIterator<TOut>(SeqIteratorHandler<TIn, TOut> handler)
        {
            return new Flow<TIn, TOut>(new ScatterStage<TIn, TOut>(handler));
        }


        public static Flow<TIn, TIn> Copy()
        {
            return new Flow<TIn, TIn>((i, pi) => pi.Post(i));
        }
    }


    public class Flow<TIn, TOut>
    {
        private readonly StageBase head;


        internal Flow(ISeqHandler<TIn, TOut> handler)
            : this(handler.Handle) { }

        internal Flow(Func<TIn, TOut> handler)
            : this((i, po) => po.Post(handler(i))) {}

        internal Flow(SeqHandler<TIn, TOut> handler)
        {
            this.head = new SeqStage<TIn, TOut>(handler);
        }

        internal Flow(SeqIteratorHandler<TIn, TOut> handler)
        {
            this.head = new SeqStage<TIn, TOut>(handler);
        }

        internal Flow(StageBase head)
        {
            this.head = head;
        }



        public Flow<TIn, TOutNext> Do<TOutNext>(ISeqHandler<TOut, TOutNext> handler)
        {
            return Do<TOutNext>(handler.Handle);
        }

        public Flow<TIn, TOutNext> Do<TOutNext>(Func<TOut, TOutNext> handler)
        {
            return Do<TOutNext>((i, po) => po.Post(handler(i)));
        }

        public Flow<TIn, TOutNext> Do<TOutNext>(SeqHandler<TOut, TOutNext> handler)
        {
            this.head
                .TransitiveHull.First()
                .ContinueWith(0, new SeqStage<TOut, TOutNext>(handler));
            return new Flow<TIn, TOutNext>(this.head);
        }

        public Flow<TIn, TOutNext> DoWithIterator<TOutNext>(SeqIteratorHandler<TOut, TOutNext> handler)
        {
            this.head
                .TransitiveHull.First()
                .ContinueWith(0, new SeqStage<TOut, TOutNext>(handler));
            return new Flow<TIn, TOutNext>(this.head);
        }


        public Flow<TIn, TOutNext> Concat<TOutNext>(Flow<TOut, TOutNext> flow)
        {
            this.head
                .TransitiveHull.First()
                .ContinueWith(0, flow.head);
            return new Flow<TIn, TOutNext>(this.Head);
        }


        public ForkedFlow<TIn, TOut, TOut> Fork()
        {
            return Fork<TOut>((i, po0, po1) =>
                                  {
                                      po0.Post(i);
                                      po1.Post(i);
                                  });
        }

        public ForkedFlow<TIn, TBranches, TBranches> Fork<TBranches>(ForkHandler<TOut, TBranches, TBranches> handler)
        {
            return Fork<TBranches, TBranches>(handler);
        }

        public ForkedFlow<TIn, TBranch0, TBranch1> Fork<TBranch0, TBranch1>(ForkHandler<TOut, TBranch0, TBranch1> handler)
        {
            this.head
                .TransitiveHull.First()
                .ContinueWith(0, new ForkStage<TOut, TBranch0, TBranch1>(handler));
            return new ForkedFlow<TIn, TBranch0, TBranch1>(this.head);
        }



        public Flow<TIn, TOutNext> Scatter<TOutNext>(ISeqHandler<TOut, TOutNext> handler)
        {
            return Scatter<TOutNext>(handler.Handle);
        }

        public Flow<TIn, TOutNext> Scatter<TOutNext>(Func<TOut, TOutNext> handler)
        {
            return Scatter<TOutNext>((i, po) => po.Post(handler(i)));
        }

        public Flow<TIn, TOutNext> Scatter<TOutNext>(SeqHandler<TOut, TOutNext> handler)
        {
            this.head
                .TransitiveHull.First()
                .ContinueWith(0, new ScatterStage<TOut, TOutNext>(handler));
            return new Flow<TIn, TOutNext>(this.head);
        }

        public Flow<TIn, TOutNext> ScatterWithIterator<TOutNext>(SeqIteratorHandler<TOut, TOutNext> handler)
        {
            this.head
                .TransitiveHull.First()
                .ContinueWith(0, new ScatterStage<TOut, TOutNext>(handler));
            return new Flow<TIn, TOutNext>(this.head);
        }


        internal StageBase Head
        {
            get { return this.head; }
        }


        public Port<TIn> Input
        {
            get
            {
                return (Port<TIn>)this.head.Input.First();
            }
        }

        public Port<TOut> Output
        {
            get
            {
                return (Port<TOut>)this.head
                            .TransitiveHull.First()
                            .Output.First();
            }
        }


        public void Receive(Handler<TOut> handler)
        {
            Receive(true, handler);
        }

        public void Receive(bool repeatedly, Handler<TOut> handler)
        {
            Arbiter.Activate(
                new DispatcherQueue(),
                Arbiter.Receive(
                    repeatedly,
                    this.Output,
                    handler
                    )
                );
        }
    }
}
